#include "ButtonItem.h"

static const int s_dragThreshold = 64;

ButtonItem::~ButtonItem()
{
	if (_pad)
	{
		_pad->removeChildSnatch(this);
	}
}

bool ButtonItem::init(Node* node)
{
	_nodeTarget = node;
	setState(eState::Normal);
	return true;
}


void ButtonItem::setEnabled(bool isEnabled)
{
	if (_isEnabled == isEnabled)
	{
		return;
	}
	_isEnabled = isEnabled;
	if (!isEnabled)
	{
		setState(eState::None);
	}
}


Rect ButtonItem::getBox()
{
	const auto& size = _nodeTarget->getContentSize();
	return Rect(0, 0, size.width, size.height);
}


PadItem::~PadItem()
{
	forr(_buttons, i)
	{
		if (_buttons[i])
		{
			_buttons[i]->_pad = nullptr;
		}
	}
	if (_palListener)
	{
		_palListener->removeChildSnatch(this);
	}
}

bool PadItem::init(Node* node)
{
	return ButtonItem::init(node);
}


void PadItem::addChildSnatch(ButtonItem *btn, bool isAddchild /* = true */)
{
	btn->_pad = this;
	_buttons.push_back(btn);
	if (isAddchild)
	{
		_nodeTarget->addChild(btn->_nodeTarget);
	}
}


void PadItem::removeChildSnatch(ButtonItem *btn)
{
	if (_palListener && _palListener->_btn == btn)
	{
		_palListener->_btn = nullptr;
	}
	forr(_buttons, i)
	{
		if (_buttons[i] == btn)
		{
			_buttons.erase(_buttons.begin() + i);
			return;
		}
	}
}


ButtonItem* PadItem::getButton(const Vec2& location)
{
	for (const auto& btn : _buttons)
	{
		if (!btn->_nodeTarget->isVisible())
		{
			continue;
		}
		if (btn->getBox().containsPoint(btn->_nodeTarget->convertToNodeSpace(location)))
		{
			return btn;
		}
	}
	return nullptr;
}

void PadItem::doVisible(bool visible)
{
	_nodeTarget->setVisible(visible);
	if (visible)
	{
		_nodeTarget->setLocalZOrder(++_palListener->_z);
	}
}




PadListener::~PadListener()
{
	forr(_sortPads, i)
	{
		if (_sortPads[i])
		{
			_sortPads[i]->_palListener = nullptr;
		}
	}
}



bool PadListener::init(Node* node)
{
	if (!EventListenerTouchOneByOne::init())
	{
		return false;
	}
	_shedulerKey = toString("%p", this);

#ifdef ccc_mouse
	EventListenerMouse* mouse = EventListenerMouse::create();
	static Vec2 vecCursor;
	mouse->onMouseMove = [&](EventMouse* e)
	{
		vecCursor = Vec2(e->getCursorX(), e->getCursorY());
		if (setPadForMouse(vecCursor))
		{
			setButtonForMouse(vecCursor);
		}
	};

	mouse->onMouseUp = [&](EventMouse* e)
	{
	 	if (e->getMouseButton() != EventMouse::MouseButton::BUTTON_RIGHT)
	 	{
	 		return;
	 	}
		if (!_padTop || !_padTop->isEnabled() || _btn)
	 	{
	 		return;
	 	}
	 	if (_padTop->_isCloseRup)
	 	{
			_padTop->doVisible(false);
		}
 	};

	mouse->onMouseScroll = [&](EventMouse* e)
	{
		if (!_padTop || !_padTop->isEnabled() || _padTop->_dragType == PadItem::eDrag::None || _padTop->_scrollSpeed == 0)
		{
			return;
		}
		Node* nodeDrag = _padTop->getDragableNode();
		switch (_padTop->_dragType)
		{
		case PadItem::eDrag::H:
			nodeDrag->setPositionX(nodeDrag->getPositionX() + _padTop->_scrollSpeed * e->getScrollY());
			if (setPadForMouse(vecCursor))
			{
				setButtonForMouse(vecCursor);
			}
			_padTop->doDraging();
			break;
		default:
			nodeDrag->setPositionY(nodeDrag->getPositionY() + _padTop->_scrollSpeed * e->getScrollY());
			if (setPadForMouse(vecCursor))
			{
				setButtonForMouse(vecCursor);
			}
			_padTop->doDraging();
			break;
		}
	};

	node->getEventDispatcher()->addEventListenerWithSceneGraphPriority(mouse, node);
#endif 

	EventListenerTouchOneByOne *oneByone = this; // EventListenerTouchOneByOne::create();
	oneByone->setSwallowTouches(true);
	oneByone->onTouchBegan = [&](Touch*t, Event* e)
	{
		_delta = 0;
		_isDragThreshold = false;
//		_padTop = getPadTop(t->getLocation());
		setPadForTouch(t->getLocation());
		if (!_padTop || !_padTop->_isEnabled)
		{
			return false;
		}
		_padTop->_nodeTarget->setLocalZOrder(++_z);
		resortPad();
		setButtonForTouch(t->getLocation());
		if (!_isSheduling)
		{
			EventListenerTouchOneByOne::getAssociatedNode()->schedule(CC_CALLBACK_1(PadListener::update, this), _shedulerKey);
			_isSheduling = true;
		}
		return true;
	};

	oneByone->onTouchMoved = [&](Touch*t, Event* e)
	{
		if (!_padTop || !_padTop->isEnabled() || _padTop->_dragType == PadItem::eDrag::None)
		{
			return;
		}
		if (!_isDragThreshold)
		{
			if (t->getLocation().distance(t->getStartLocation()) > s_dragThreshold)
			{
				_isDragThreshold = true;
				_vecDrag = _padTop->getDragableNode()->getPosition();
				_vecStartLocation = t->getLocation();
			}
			return;
		}
		Node* nodeDrag = _padTop->getDragableNode();
		vecCursor = nodeDrag->getParent()->convertToNodeSpace(t->getLocation());
		vecCursor -= nodeDrag->getParent()->convertToNodeSpace(_vecStartLocation);
		vecCursor += _vecDrag;
//		vecCursor = nodeDrag->getPosition() + nodeDrag->getParent()->convertToNodeSpace(t->getDelta());
		switch (_padTop->_dragType)
		{
		case PadItem::eDrag::V:
			nodeDrag->setPositionY(vecCursor.y);
			_padTop->doDraging();
			break;
		case PadItem::eDrag::H:
			nodeDrag->setPositionX(vecCursor.x);
			_padTop->doDraging();
			break;
		default:
			nodeDrag->setPosition(vecCursor);
			_padTop->doDraging();
			break;
		}
	};


	oneByone->onTouchCancelled = oneByone->onTouchEnded = [&](Touch*t, Event* e)
	{
		if (_isSheduling)
		{
			EventListenerTouchOneByOne::getAssociatedNode()->unschedule(_shedulerKey);
			_isSheduling = false;
		}
		if (!_padTop || !_padTop->isEnabled())
		{
			return;
		}
		_padTop->setState(ButtonItem::eState::Normal);

		ButtonItem *btnCurr = _padTop->getButton(t->getLocation());
		forr(_sortPads, i)
		{
			PadItem* pad = _sortPads.at(i);
			for (const auto& btn : pad->_buttons)
			{
				if (!btn->_nodeTarget->isVisible())
				{
					continue;
				}
				if (!btn->_isEnabled)
				{
					continue;
				}
				if (pad == _padTop && btn == btnCurr)
				{
					continue;
				}
				btn->doClick(t->getLocation(), false);
			}
		}
		bool isButtonClicking = false;
		if (btnCurr && btnCurr == _btn)
		{
			if (!_isDragThreshold && btnCurr->isEnabled())
			{
				isButtonClicking = true;
				Node* parent = btnCurr->_nodeTarget->getParent();
				btnCurr->doClick(t->getLocation(), true);
				if (parent->getChildren().find(btnCurr->_nodeTarget) == parent->getChildren().end())
				{
					_btn = btnCurr = nullptr;
				}

				if (btnCurr)
				{
					btnCurr->setState(ButtonItem::eState::Normal);
				}
			}
		}
		_padTop->doClick(t->getLocation(), isButtonClicking);

		if (btnCurr != _btn)
		{
			if (_btn && _btn->isEnabled())
			{
				_btn->setState(ButtonItem::eState::Normal);
			}
			_btn = btnCurr;
		}
	};
	node->getEventDispatcher()->addEventListenerWithSceneGraphPriority(oneByone, node);
	
	return true;
}


void PadListener::addChildSnatch(PadItem* pad, bool isAddchild /* = true */)
{
	_sortPads.push_back(pad);
	pad->_palListener = this;
	if (isAddchild)
	{
		EventListenerTouchOneByOne::getAssociatedNode()->addChild(pad->_nodeTarget);
		pad->_nodeTarget->setLocalZOrder(++_z);
	}
	resortPad();
}



void PadListener::removeChildSnatch(PadItem *pad)
{
	if (_padTop == pad)
	{
		_padTop = nullptr;
	}
	forr(pad->_buttons, x)
	{
		if (_btn == pad->_buttons[x])
		{
			_btn = nullptr;
			break;
		}
	}
	forr(_sortPads, i)
	{
		if (_sortPads[i] == pad)
		{
			_sortPads.erase(_sortPads.begin() + i);
			return;
		}
	}
}


void PadListener::resortPad()
{
	std::sort(_sortPads.begin(), _sortPads.end(), [](PadItem* n1, PadItem* n2)
	{
		return n1->_nodeTarget->getLocalZOrder() < n2->_nodeTarget->getLocalZOrder(); 
	});
}


PadItem* PadListener::getPadTop(const Vec2& location)
{
	forr(_sortPads, i)
	{
		auto *pad = _sortPads.at(i);
		if (pad->getBox().containsPoint(pad->_nodeTarget->convertToNodeSpace(location)))
		{
			return pad;
		}
// 		const auto& size = node->getContentSize();
// 		if (node->isVisible() && Rect(0,0,size.width, size.height).containsPoint(node->convertToNodeSpace(location)))
// 		{
// 			return static_cast<PadItem*>(node->getUserData());
// 		}
	}
	return nullptr;
}



bool PadListener::setPadForMouse(const Vec2& location)
{
	PadItem* padCurr = getPadTop(location);
	if (padCurr == _padTop)
	{
		return true;
	}
	if (_padTop)
	{
		if (_padTop->isEnabled())
		{
			_padTop->setState(ButtonItem::eState::Normal);
		}
	}
	if (padCurr)
	{
		if (padCurr->isEnabled())
		{
			padCurr->setState(ButtonItem::eState::Cross);
		}
	}
	_padTop = padCurr;
	return true;
}



bool PadListener::setButtonForMouse(const Vec2& location)
{
	ButtonItem* btnCurr = _padTop->getButton(location);
	if (btnCurr == _btn)
	{
		return false;
	}
	if (_btn)
	{
		if (_btn->isEnabled())
		{
			_btn->setState(ButtonItem::eState::Normal);
		}
	}
	if (btnCurr)
	{
		if (btnCurr->isEnabled())
		{
			btnCurr->setState(ButtonItem::eState::Cross);
		}
	}
	_btn = btnCurr;
	return true;
}



bool PadListener::setPadForTouch(const Vec2& location)
{
	PadItem* padCurr = getPadTop(location);
	if (padCurr == _padTop)
	{
		if (padCurr && padCurr->isEnabled())
		{
			padCurr->setState(ButtonItem::eState::Down);
		}
		return true;
	}
	if (_padTop)
	{
		if (_padTop->isEnabled())
		{
			_padTop->setState(ButtonItem::eState::Normal);
		}
	}
	if (padCurr)
	{
		if (padCurr->isEnabled())
		{
			padCurr->setState(ButtonItem::eState::Down);
		}
	}
	_padTop = padCurr;
	return true;
}

bool PadListener::setButtonForTouch(const Vec2& location)
{
	ButtonItem *btnCurr = _padTop->getButton(location);
	if (btnCurr == _btn)
	{
		if (btnCurr && btnCurr->isEnabled())
		{
			btnCurr->setState(ButtonItem::eState::Down);
		}
		return true;
	}
	if (_btn)
	{
		if (_btn->isEnabled())
		{
			_btn->setState(ButtonItem::eState::Normal);
		}
	}
	if (btnCurr)
	{
		if (btnCurr->isEnabled())
		{
			btnCurr->setState(ButtonItem::eState::Down);
		}
	}
	_btn = btnCurr;
	return true;
}


void PadListener::update(float delta)
{
	_delta += delta;
	if (_padTop && _padTop->isEnabled())
	{
		Node *parent = _padTop->_nodeTarget->getParent();
		_padTop->doKeep(_delta);
		if (parent->getChildren().find(_padTop->_nodeTarget) == parent->getChildren().end())
		{
			_padTop = nullptr;
		}
	}
	if (_btn && _btn->isEnabled())
	{
		Node *parent = _btn->_nodeTarget->getParent();
		_btn->doKeep(_delta);
		if (parent->getChildren().find(_btn->_nodeTarget) == parent->getChildren().end())
		{
			_btn = nullptr;
		}
	}
}


